package com.corpgovernment.common.requestprocess;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.StringJoiner;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.lang3.StringUtils;

import com.corpgovernment.common.common.CorpBusinessException;
import com.corpgovernment.common.logging.LogUtil;
import com.corpgovernment.common.utils.Null;

/**
 * 日志上下文工具类
 * 
 * @author dlz
 * @since 2024/3/20
 */
public final class LogContextUtil {
    /**
     * 接口名
     */
    private static final String ACTION_NAME = "actionName";

    /**
     * 接口处理开始时间
     */
    private static final String START_TIME = "startTime";

    /**
     * 服务异常
     */
    private static final String SERVER_EXCEPTION = "serverException";

    private static final ThreadLocal<Map<String, Object>> CURRENT_CONTEXT = new ThreadLocal<>();

    private LogContextUtil() {}

    /**
     * 初始化上下文
     */
    public static void initContext() {
        CURRENT_CONTEXT.set(new ConcurrentHashMap<>());
    }

    /**
     * 移除上下文
     */
    public static void remove() {
        CURRENT_CONTEXT.remove();
    }

    /**
     * put值
     */
    public static void put(String key, Object value) {
        Map<String, Object> contextMap = CURRENT_CONTEXT.get();
        if (contextMap != null) {
            contextMap.put(key, value);
        }
    }

    /**
     * 获取值
     */
    @SuppressWarnings("unchecked")
    public static <E> E get(String key, Class<E> clazz) {
        Map<String, Object> contextMap = CURRENT_CONTEXT.get();
        if (contextMap == null) {
            return null;
        }
        return (E)contextMap.get(key);
    }

    /**
     * 获取接口名
     */
    public static String getActionName() {
        return Null.or(get(ACTION_NAME, String.class), StringUtils.EMPTY);
    }

    /**
     * 设置接口名
     */
    public static void setActionName(String actionName) {
        put(ACTION_NAME, actionName);
    }

    /**
     * 获取接口处理开始时间
     */
    public static LocalDateTime getStartTime() {
        return get(START_TIME, LocalDateTime.class);
    }

    /**
     * 设置接口处理开始时间
     */
    public static void setStartTime(LocalDateTime startTime) {
        put(START_TIME, startTime);
    }

    /**
     * 获取接口处理开始时间到当前时间的耗时(单位：ms)
     */
    public static int getDuration() {
        LocalDateTime startTime = getStartTime();
        if (startTime == null) {
            return 0;
        }
        LocalDateTime end = LocalDateTime.now();
        return (int)Duration.between(startTime, end).toMillis();
    }

    /**
     * 设置运行中抛出的异常
     */
    public static void setServerException(Exception th) {
        put(SERVER_EXCEPTION, th);

    }

    /**
     * 获取运行中抛出的异常
     */
    public static Exception getServerException() {
        return get(SERVER_EXCEPTION, Exception.class);
    }

    /**
     * 获取运行中抛出的CorpBusinessException异常的code
     */
    public static String getCorpBusinessExceptionCode() {
        Exception throwable = get(SERVER_EXCEPTION, Exception.class);
        if (throwable instanceof CorpBusinessException) {
            return ((CorpBusinessException)throwable).getResultCode().toString();
        }
        return StringUtils.EMPTY;
    }

    /**
     * 获取运行中抛出的异常名
     */
    public static String getServerExceptionName() {
        Exception serverException = getServerException();
        if (serverException != null) {
            return serverException.getClass().getName();
        }
        return StringUtils.EMPTY;
    }

    /**
     * 获取运行中抛出的异常信息
     */
    public static String getServerExceptionMessage() {
        Exception serverException = getServerException();
        if (serverException != null) {
            StringJoiner sj = new StringJoiner(System.lineSeparator());
            sj.add(serverException.getMessage());
            sj.add(LogUtil.getException(serverException));
            return sj.toString();
        }
        return StringUtils.EMPTY;
    }

    /**
     * 包装Runnable,以在多线程中传递上下文
     */
    public static Runnable wrap(Runnable runnable) {
        Map<String, Object> contextMap = LogContextUtil.CURRENT_CONTEXT.get();
        return () -> {
            try {
                LogContextUtil.CURRENT_CONTEXT.set(contextMap);
                runnable.run();
            } finally {
                LogContextUtil.remove();
            }
        };
    }
}
